package com.dymall.user.service.impl;

import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dymall.common.domain.Result;
import com.dymall.common.exception.BadRequestException;
import com.dymall.common.exception.BizIllegalException;
import com.dymall.common.exception.ForbiddenException;
import com.dymall.common.utils.UserContext;
import com.dymall.user.config.JwtProperties;
import com.dymall.user.domain.dto.LoginFormDTO;
import com.dymall.user.domain.po.User;
import com.dymall.user.domain.vo.UserLoginVO;
import com.dymall.user.enums.UserStatus;
import com.dymall.user.mapper.UserMapper;
import com.dymall.user.service.UserService;
import com.dymall.user.utils.JwtTool;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.concurrent.TimeUnit;

@Slf4j
@Service
@RequiredArgsConstructor
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    private final JwtTool jwtTool;
    private final JwtProperties jwtProperties;
    private final PasswordEncoder passwordEncoder;
    private final StringRedisTemplate stringRedisTemplate;

    @Override
    public UserLoginVO login(LoginFormDTO loginDTO) {
        // 1.数据校验
        String username = loginDTO.getUsername();
        String password = loginDTO.getPassword();
        // 2.根据用户名或手机号查询
        User user = lambdaQuery().eq(User::getUsername, username).one();
        if(user == null){
            throw new ForbiddenException("用户不存在");
        }
        // 3.校验是否禁用
        if(user.getStatus() == UserStatus.FROZEN){
            throw new ForbiddenException("用户被冻结");//无权限访问异常
        }
        //4.校验密码
        if (!passwordEncoder.matches(password, user.getPassword())) {
            throw new BadRequestException("用户名或密码错误");
        }

        /*
        if(!user.getPassword().equals(password)){
            throw new BadRequestException("用户名或密码错误");
        }

         */
        // 5.生成TOKEN
        String token = jwtTool.createToken(user.getId(), jwtProperties.getTokenTTL());
//        // 5.1、将TOKEN,UserMap存储到redis中
//        Map<String, Object> userMap = BeanUtil.beanToMap(user, new HashMap<>(),
//                CopyOptions.create()
//                        .setIgnoreNullValue(true)
//                        .setFieldValueEditor((fieldName, fieldValue) -> fieldValue.toString()));
        String key = "login:token:" + token;
        stringRedisTemplate.opsForValue().set(key, user.getId().toString());
        stringRedisTemplate.expire(key, 20, TimeUnit.MINUTES);//为key设置过期时间

        // 6.封装VO返回
        return new UserLoginVO(token, user.getId(), user.getUsername(), user.getBalance());
    }

    @Transactional
    @Override
    public void deduct(String password, Integer amount) {
        log.info("开始扣款");
        // 1.校验密码
        User user = lambdaQuery().eq(User::getId, UserContext.getUser()).one();
        if(!passwordEncoder.matches(password, user.getPassword())){
            // 密码错误
            throw new BizIllegalException("用户密码错误");
        }
        // 2.尝试扣款
        int newBalance = user.getBalance() - amount;
        try{
            lambdaUpdate().set(newBalance > 0, User::getBalance, newBalance).eq(User::getId, UserContext.getUser()).update();
        }
        catch (Exception e){
            throw new RuntimeException("扣款失败，可能是余额不足！", e);
        }
        log.info("扣款成功");
    }

    @Override
    public UserLoginVO loginByPhone(String phone, String myCode) {
        // 1、检验电话号码是否合法
        if(StrUtil.isBlank(phone) || !phone.matches("^1([38][0-9]|4[579]|5[0-3,5-9]|6[6]|7[0135678]|9[89])\\d{8}$")){
            throw new ForbiddenException("电话号码或验证码错误！");
        }
        // 2、查找电话号码在数据库中是否存在
        User user = lambdaQuery().eq(User::getPhone, phone).one();
        if(user == null){
            // 2.1、不存在，创建用户/抛异常
            throw new ForbiddenException("用户不存在！");
        }
        // 3、从redis中获取验证码
        String code = stringRedisTemplate.opsForValue().get("login:code:" + phone);
        // 4、检验验证码
        if(myCode.isBlank() || !myCode.equals(code)){
            throw new ForbiddenException("电话号码或验证码错误！");
        }
        // 5.生成TOKEN
        String token = jwtTool.createToken(user.getId(), jwtProperties.getTokenTTL());
        // 6.封装VO返回
        return new UserLoginVO(token, user.getId(), user.getUsername(), user.getBalance());
    }

    @Override
    public Result<String> sendCode(String phone) {
        // 1、检验电话号码是否合法
        if(StrUtil.isBlank(phone) || !phone.matches("^1([38][0-9]|4[579]|5[0-3,5-9]|6[6]|7[0135678]|9[89])\\d{8}$")){
            throw new ForbiddenException("电话号码或验证码错误！");
        }
        // 2、查找电话号码在数据库中是否存在
        User user = lambdaQuery().eq(User::getPhone, phone).one();
        if(user == null){
            // 2.1、不存在，创建用户/抛异常
            throw new ForbiddenException("用户不存在！");
        }
        // 3、生成验证码
        String code = RandomUtil.randomNumbers(6);
        // 4、存储到redis
        stringRedisTemplate.opsForValue().set("login:code:"+phone, code, 1, TimeUnit.MINUTES);
        // 5、模拟发送验证码
        log.info("验证码为{}",code);
        return Result.ok("验证码已发送");
    }
}
